package service

import (
	"context"
	"encoding/json"
	"errors"
	"github.com/wonderivan/logger"
	corev1 "k8s.io/api/core/v1"
	metav1 "k8s.io/apimachinery/pkg/apis/meta/v1"
	"k8s.io/apimachinery/pkg/util/intstr"
	"strconv"
	"strings"
	"time"
)

var Servicev1 servicev1

type servicev1 struct{}
type ServicesResp struct {
	Total int              `json:"total"`
	Items []corev1.Service `json:"items"`
}

type serviceCell corev1.Service

func (s serviceCell) GetCreation() time.Time {
	return s.CreationTimestamp.Time
}

func (s serviceCell) GetName() string {
	return s.Name
}

func (s *servicev1) toCells(servicess []corev1.Service) []DataCell {
	cells := make([]DataCell, len(servicess))
	for i := range servicess {
		cells[i] = serviceCell(servicess[i])
	}
	return cells
}
func (s *servicev1) fromCells(cells []DataCell) []corev1.Service {
	servicess := make([]corev1.Service, len(cells))
	for i := range servicess {
		servicess[i] = corev1.Service(cells[i].(serviceCell))
	}
	return servicess
}

func (s *servicev1) GetServices(namespace,filterName string, limit, page int) (servicesResp *ServicesResp, err error) {
	servicesList, err := K8s.ClientSet.CoreV1().Services(namespace).List(context.TODO(), metav1.ListOptions{})
	if err != nil {
		logger.Error(errors.New("获取service列表失败," + err.Error()))
		return nil, errors.New("获取service列表失败," + err.Error())
	}
	selectableData := &dataSelector{
		GenericDataList: s.toCells(servicesList.Items),
		DataSelect: &DataSelectQuery{
			Filter: &FilterQuery{Name: filterName},
			Paginate: &PaginateQuery{
				Limit: limit,
				Page:  page,
			},
		},
	}
	filtered := selectableData.Filter()
	data := filtered.Sort().Paginate()
	total := len(data.GenericDataList)
	servicess := s.fromCells(data.GenericDataList)
	return &ServicesResp{
		Total: total,
		Items: servicess,
	}, nil
}

func (s *servicev1) GetServiceDetail(servicesname, namespace string) (servicedata *corev1.Service, err error) {
	servicedata, err = K8s.ClientSet.CoreV1().Services(namespace).Get(context.TODO(), servicesname, metav1.GetOptions{})
	if err != nil {
		logger.Error(errors.New("get services detail failed," + err.Error()))
		return nil, errors.New("get services detail failed," + err.Error())
	}
	// json.Marshal()将对象序列化为字节数组，如下所示：
	//bt := []byte{104,101,108,108,111,32,119,111,114,108,100}
	//fmt.Println(string(bt)) // 输出 hello world

	/*
		两种方法将 *v1.service 类型转换为yaml格式输出出来，第一种是先转成json，然后json转yaml，如下所示：
			data,err := json.Marshal(services)
			if err != nil{
				logger.Error("json failed...")
			}
			y,err := yaml.JSONToYAML(data)
			if err != nil{
				logger.Error("json to yaml failed")
			}
			fmt.Println(string(y))
		第二种方法是直接使用yaml.Marshal方法，将*v1.service转成字节数组，然后再string()转成字符串,
			data1,err := yaml.Marshal(services)
			if err!=nil {
				logger.Error("yaml failed")
			}
			fmt.Println(string(data1))

	*/
	return servicedata, nil
}

type ServiceCreate struct {
	Namespace        string            `json:"namespace,omitempty"`
	Name             string            `json:"name,omitempty"`
	//ServiceMetaLabel map[string]string `json:"servicemetalabel,omitempty"`
	// 这里不写注释
	//Annotations      map[string]string `json:"annotations,omitempty"`
	ServicePodLabel  map[string]string `json:"servicepodlabel,omitempty"`

	// portname这是采用协议-端口的形式
	//PortName         string           `json:"portname,omitempty"`
	// protocol协议这些先用string，在下面创建的逻辑中转成corev1.protocol类型
	Protocol         string				`json:"protocol,omitempty"`
	TargetPort       int32              `json:"targetport,omitempty"`
	//强制设置端口与目标容器端口一致
	//Port             int32             `json:"port"`
	Type             string            `json:"type,omitempty"`
	NodePort         int32             `json:"nodeport,omitempty"`
}

func (s *servicev1) CreateService(data *ServiceCreate) (err error) {
	var portname string
	// portname采用协议 - 端口的形式设置.
	if data.Type == "ClusterIP"{
		portname = strings.ToLower(data.Protocol) + "-" + strconv.FormatInt(int64(data.TargetPort), 10)
	}else{
		portname = strings.ToLower(data.Protocol) + "-" + strconv.FormatInt(int64(data.NodePort), 10)
	}

	services := &corev1.Service{
		ObjectMeta: metav1.ObjectMeta{
			Name:        data.Name,
			Namespace:   data.Namespace,
			Labels:      data.ServicePodLabel,
			//Annotations: data.Annotations,
		},
		Spec: corev1.ServiceSpec{
			Selector: data.ServicePodLabel,
			Ports: []corev1.ServicePort{
				//由于是切片，所以需要多加一个大括号。
				{
					Name: portname,
					Protocol: corev1.Protocol(data.Protocol),
					TargetPort: intstr.IntOrString{
						Type:   0,
						IntVal: data.TargetPort,
					},
					Port: data.TargetPort,
					NodePort: data.NodePort,
				},
			},
			Type: corev1.ServiceType(data.Type),
		},
		Status: corev1.ServiceStatus{},
	}
	// 默认是ClusterIP，这里是判断NodePort，添加配置，适用于上面是默认配置，如果要单独添加配置的场景。
	if data.NodePort >= 30000 && data.NodePort <= 35000 && data.Type == "NodePort" {
		//这里是添加配置
		services.Spec.Ports[0].NodePort = data.NodePort
	}
	_, err = K8s.ClientSet.CoreV1().Services(data.Namespace).Create(context.TODO(), services, metav1.CreateOptions{})
	if err != nil {
		logger.Error(errors.New("创建service失败," + err.Error()))
		return errors.New("创建service失败," + err.Error())
	}
	return nil
}

// 更新services
func (s *servicev1) UpdateService(namespace, content string) (err error) {
	var service = &corev1.Service{}
	err = json.Unmarshal([]byte(content), service)
	if err != nil {
		logger.Error(errors.New("反序列化失败," + err.Error()))
		return errors.New("反序列化失败," + err.Error())
	}
	_, err = K8s.ClientSet.CoreV1().Services(namespace).Update(context.TODO(), service, metav1.UpdateOptions{})
	if err != nil {
		logger.Error(errors.New("更新service失败," + err.Error()))
		return errors.New("更新service失败," + err.Error())
	}
	return nil
}

// 删除service
func (s *servicev1) DeleteService(namespace, servicename string) (err error) {
	err = K8s.ClientSet.CoreV1().Services(namespace).Delete(context.TODO(), servicename, metav1.DeleteOptions{})
	if err != nil {
		logger.Error(errors.New("删除service失败," + err.Error()))
		return errors.New("删除service失败," + err.Error())
	}
	return nil
}
